﻿using System;
using System.Collections.Generic;
using System.Threading;
using S7.Net;
using S7PlcDataCollector.Models;
using S7PlcDataCollector.Service.PlcLink;
using S7PlcDataCollector.Service.SQLiteProxy;

namespace S7PlcDataCollector.Service
{
    public class S7PlcDataCollectorService : IPlcLinkObserver, IPlcDataObserver
    {
        public event Action<S7PlcEntity, PlcLinkState> LinkStateChangeEvent
            = (plcEntity, linkState) => { };

        public event Action<S7PlcVariableEntity, PlcDataResult> ReceivePlcDataEvent =
            (variableEntity, result) => { };

        public List<CpuType> SupportedPlcTypeList { get; }
        public List<VarType> SupportedPlcVariableTypeList { get; }
        public static readonly int S7PlcDefaultPort = 102;

        // ReSharper disable once IdentifierTypo
        private readonly SqliteDbProxy _sqliteDbProxy;

        private readonly PlcLinkManager _plcLinkManager;

        public void Init()
        {
            //在线程池中初始化
            ThreadPool.QueueUserWorkItem((state) =>
            {
                _sqliteDbProxy.Init();
                _plcLinkManager.Init();
            });
        }

        public void UnInit()
        {
            _plcLinkManager.UnInit();
        }

        #region PlcLink

        public void AddPlcLink(S7PlcEntity plcEntity)
        {
            _plcLinkManager.AddPlcEntity(plcEntity);
        }

        public void RemovePlcLink(S7PlcEntity plcEntity)
        {
            ThreadPool.QueueUserWorkItem((state) => { _plcLinkManager.RemovePlcEntity(plcEntity); });
        }

        public PlcLinkState GetLinkState(int plcEntityRid)
        {
            return _plcLinkManager.GetLinkState(plcEntityRid);
        }

        public void GetVariable(S7PlcVariableEntity variableEntity)
        {
            _plcLinkManager.GetVariable(variableEntity);
        }

        public void LinkStateChange(S7PlcEntity plcEntity, PlcLinkState linkState)
        {
            LinkStateChangeEvent?.Invoke(plcEntity, linkState);
        }

        public void ReceivePlcData(S7PlcVariableEntity variableEntity, PlcDataResult result)
        {
            ReceivePlcDataEvent?.Invoke(variableEntity, result);
        }

        #endregion

        #region Data Interfave

        /// <summary>
        /// 获取PLC实体列表
        /// </summary>
        /// <returns>PLC实体列表</returns>
        public List<S7PlcEntity> GetS7PlcEntityList()
        {
            return _sqliteDbProxy.GetS7PlcEntityList();
        }

        /// <summary>
        /// 添加PLC实体对象
        /// </summary>
        /// <param name="model">PLC实体对象</param>
        /// <returns>Rid</returns>
        public int AddS7PlcEntity(S7PlcEntity model)
        {
            return _sqliteDbProxy.AddS7PlcEntity(model);
        }

        /// <summary>
        /// 修改PLC实体对象
        /// </summary>
        /// <param name="model">PLC实体对象</param>
        /// <returns>true成功，false失败</returns>
        public bool UpdateS7PlcEntity(S7PlcEntity model)
        {
            return _sqliteDbProxy.UpdateS7PlcEntity(model);
        }

        /// <summary>
        /// 删除PLC实体对象
        /// </summary>
        /// <param name="model">PLC实体对象</param>
        /// <returns>true成功，false失败</returns>
        public bool DeleteS7PlcEntity(S7PlcEntity model)
        {
            return _sqliteDbProxy.DeleteS7PlcEntity(model);
        }

        /// <summary>
        /// 获取PLC变量实体列表
        /// </summary>
        /// <returns>PLC变量实体列表</returns>
        public List<S7PlcVariableEntity> GetS7PlcVariableEntityList()
        {
            return _sqliteDbProxy.GetS7PlcVariableEntityList();
        }

        /// <summary>
        /// 获取PLC变量实体列表
        /// </summary>
        /// <param name="s7PlcEntityRid">PLC实体Id</param>
        /// <returns>PLC变量实体列表</returns>
        public List<S7PlcVariableEntity> GetS7PlcVariableEntityList(int s7PlcEntityRid)
        {
            return _sqliteDbProxy.GetS7PlcVariableEntityList(s7PlcEntityRid);
        }

        /// <summary>
        /// 添加PLC变量实体对象
        /// </summary>
        /// <param name="model">PLC变量实体对象</param>
        /// <returns>Rid</returns>
        public int AddS7PlcVariableEntity(S7PlcVariableEntity model)
        {
            return _sqliteDbProxy.AddS7PlcVariableEntity(model);
        }

        /// <summary>
        /// 修改PLC变量实体对象
        /// </summary>
        /// <param name="model">PLC变量实体对象</param>
        /// <returns>true成功，false失败</returns>
        public bool UpdateS7PlcVariableEntity(S7PlcVariableEntity model)
        {
            return _sqliteDbProxy.UpdateS7PlcVariableEntity(model);
        }

        /// <summary>
        /// 删除PLC变量实体对象
        /// </summary>
        /// <param name="model">PLC变量实体对象</param>
        /// <returns>true成功，false失败</returns>
        public bool DeleteS7PlcVariableEntity(S7PlcVariableEntity model)
        {
            return _sqliteDbProxy.DeleteS7PlcVariableEntity(model);
        }

        #endregion

        #region Singleton Pattern

        private static S7PlcDataCollectorService _instance;
        private static readonly object LockHelper = new object();

        private S7PlcDataCollectorService()
        {
            SupportedPlcTypeList = new List<CpuType>()
            {
                CpuType.S71500, CpuType.S71200, CpuType.S7400, CpuType.S7300, CpuType.S7200
            };
            SupportedPlcVariableTypeList = new List<VarType>()
            {
                VarType.Bit, VarType.Byte, VarType.Word, VarType.DWord,
                VarType.Int, VarType.DInt, VarType.Real, VarType.LReal,
                VarType.String, VarType.S7String
            };
            _sqliteDbProxy = new SqliteDbProxy();
            _plcLinkManager = new PlcLinkManager(this, this);
        }

        public static S7PlcDataCollectorService Instance
        {
            get
            {
                if (_instance != null)
                {
                    return _instance;
                }

                lock (LockHelper)
                {
                    _instance = _instance ?? new S7PlcDataCollectorService();
                }

                return _instance;
            }
        }

        #endregion
    }
}
